package com.zhan.signalling.server;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang3.StringUtils;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.corundumstudio.socketio.Configuration;
import com.corundumstudio.socketio.SocketIONamespace;
import com.corundumstudio.socketio.SocketIOServer;
import com.corundumstudio.socketio.listener.DataListener;
import com.corundumstudio.socketio.namespace.Namespace;
import com.corundumstudio.socketio.store.RedissonStoreFactory;
import com.google.common.base.Splitter;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.zhan.signalling.connect.listener.ZhanConnectListener;
import com.zhan.signalling.connect.listener.ZhanDisconnectListener;
import com.zhan.signalling.exception.listener.ZhanExceptionListener;
import com.zhan.signalling.message.AccoutType;
import com.zhan.signalling.message.Message;
import com.zhan.signalling.store.StoreMessageFactory;
import com.zhan.signalling.util.RedisUtil;

public class SocketServer {
	
	private static final Logger LOG = LoggerFactory.getLogger(SocketServer.class); 
	
	public final static String serverConfigFile = "/media/knight/F860283B3235A677/signalling/zhan-signalling/config/server.properties";
	
	public final static String listenerConfigFile = "/media/knight/F860283B3235A677/signalling/zhan-signalling/config/listener.properties";
	
	public final static String PLATFORM_PROP = "platform";
	
	public final static String STORE_SESSION_PROP = "store.session";
	
	public final static String STORE_MESSAGE_PROP = "store.message";
	
	private static Properties serverProperties = null;
	
	private static Properties listenerProperties = null;
	
	/**
	 * 平台信息
	 */
	public static List<String> namespaceList;
	
	/**
	 * 角色权限
	 */
	public static Map<AccoutType,Set<Object>> permissionMap;
	
	public static SocketIOServer server = null;
	
	public static RedissonClient redissonClient = null;
	
	public static void main(String[] args) throws IOException, IllegalAccessException, InvocationTargetException {
		
		// 读取参数
		if(args.length <= 2) {
			for (int i = 0; i < args.length; i++) {
				if(args[i].equalsIgnoreCase("--c")) {
					serverProperties = new Properties();
					BufferedReader configReader = new BufferedReader(new FileReader(args[i++]));
					serverProperties.load(configReader);
					continue;
				}else if(args[i].equalsIgnoreCase("--listener")) {
					listenerProperties = new Properties();
					BufferedReader configReader = new BufferedReader(new FileReader(args[i++]));
					listenerProperties.load(configReader);
					continue;
				}
			}
		}
		
		// 使用server默认配置
		if(serverProperties == null) {
			serverProperties = new Properties();
			BufferedReader configReader = new BufferedReader(new FileReader(serverConfigFile));
			serverProperties.load(configReader);
		}
		LOG.info("serverProperties:" + serverProperties.toString());
		// 使用listener默认配置
		if(listenerProperties == null) {
			listenerProperties = new Properties();
			BufferedReader configReader = new BufferedReader(new FileReader(listenerConfigFile));
			listenerProperties.load(configReader);
		}
		LOG.info("listenerProperties:" + listenerProperties.toString());
		// 加载服务器配置
		InputStream sslConfigStream = null;
		Set<String> keys = serverProperties.stringPropertyNames();
		Configuration config = new Configuration();
		for (String key : keys) {
			if("keyStoreFile".equalsIgnoreCase(key)) {
				try {
					sslConfigStream = SocketServer.class.getResourceAsStream(serverProperties.getProperty(key));
					config.setKeyStore(sslConfigStream);
				} catch (Exception e2) {
					e2.printStackTrace();
				}finally {
					sslConfigStream.close();
				}
				continue;
			}
			BeanUtils.copyProperty(config, key, serverProperties.getProperty(key));
		}
		// 初始化信息存储工厂
		StoreMessageFactory.initStoreMessageFactory(serverProperties);
		// 读取配置文件设置session存储工厂
		setSessionStore(config);
		// 初始化redisUtil
		RedisUtil.init(redissonClient);
		// 创建服务端
		server = new SocketIOServer(config);
		// 连接请求监听
		server.addConnectListener(new ZhanConnectListener());
		// 客户端断开监听
		server.addDisconnectListener(new ZhanDisconnectListener());
		// 异常处理
		server.addListeners(new ZhanExceptionListener());
		
		//初始化namespace
		String platforms = serverProperties.getProperty(SocketServer.PLATFORM_PROP);
		if(StringUtils.isNotBlank(platforms)) {
			namespaceList = Splitter.on(",").omitEmptyStrings().trimResults().splitToList(platforms);
			//添加namespace并绑定事件
			addNameSpaces(namespaceList);
		}else {
			// 默认namespace绑定监听器
			addNameSpace(Namespace.DEFAULT_NAME);
		}
		//初始化角色权限
		initPermission();
		// 启动服务
		server.start();
	}
	
	/**
	 * 添加namespaces
	 * @param namespaceList
	 */
	public static void addNameSpaces(List<String> namespaceList) {
		for (int i = 0; i < namespaceList.size(); i++) {
			String namespace = namespaceList.get(i);
			SocketIONamespace socketIONamespace = server.addNamespace("/" + namespace);
			//绑定事件
			bindEvent(socketIONamespace);
		}
	}
	
	/**
	 * 添加namespace
	 * @param namespace
	 */
	public static void addNameSpace(String namespace) {
		SocketIONamespace socketIONamespace = server.addNamespace("/" + namespace);
		//绑定事件
		bindEvent(socketIONamespace);
	}
	
	
	/**
	 * 给namespace添加事件
	 * @param socketIONamespace
	 * @param listenerProperties
	 */
	@SuppressWarnings("unchecked")
	private static void bindEvent(SocketIONamespace socketIONamespace) {
		Set<Map.Entry<Object,Object>> listeners = listenerProperties.entrySet();
		listeners.forEach(x->{
			try {
				String listenerClass = String.valueOf(x.getValue());
				DataListener<Message> listener;
				listener = (DataListener<Message>)Class.forName(listenerClass).newInstance();
				socketIONamespace.addEventListener(x.getKey().toString(), Message.class, 
						listener);
			} catch (InstantiationException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (ClassNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		});
	}
	
	/**
	 * 初始化角色权限
	 */
	private static void initPermission() {
		// init accoutType permission
		permissionMap = Maps.newHashMap();
		String managerPermission = serverProperties.getProperty(AccoutType.MANAGER.getName());
		Set<Object> allEvents = listenerProperties.keySet();
		if("*".equals(managerPermission)) {
			permissionMap.put(AccoutType.MANAGER, allEvents);
		}else {
			Set<Object> events = Sets.newHashSet(Splitter.on(",").omitEmptyStrings().trimResults().split(managerPermission));
			permissionMap.put(AccoutType.MANAGER, events);
		}
		String participantPermission = serverProperties.getProperty(AccoutType.PARTICIPANT.getName());
		if(StringUtils.isBlank(participantPermission)) {
			permissionMap.put(AccoutType.PARTICIPANT, allEvents);
		}else {
			Set<Object> events = Sets.newHashSet(Splitter.on(",").omitEmptyStrings().trimResults().split(participantPermission));
			permissionMap.put(AccoutType.PARTICIPANT, events);
		}
		String guestPermission = serverProperties.getProperty(AccoutType.GUEST.getName());
		if(StringUtils.isBlank(guestPermission)) {
			permissionMap.put(AccoutType.GUEST, Sets.newHashSet());
		}else {
			Set<Object> events = Sets.newHashSet(Splitter.on(",").omitEmptyStrings().trimResults().split(guestPermission));
			permissionMap.put(AccoutType.GUEST, events);
		}
	}
	
	/**
	 * 设置Session存储方式
	 * @param config
	 */
	private static void setSessionStore(Configuration config) {
		String storeSession = serverProperties.getProperty(SocketServer.STORE_SESSION_PROP);
		// 获取redis配置，设置session数据存储方式；默认内存存储
		if(StringUtils.isNotBlank(storeSession) && storeSession.equalsIgnoreCase("redis")) {
			String host = serverProperties.getProperty(SocketServer.STORE_SESSION_PROP+".redis.host");
			String password = serverProperties.getProperty(SocketServer.STORE_SESSION_PROP+".redis.password");
			String database = serverProperties.getProperty(SocketServer.STORE_SESSION_PROP+".redis.database");
			String connectPoolSize = serverProperties.getProperty(SocketServer.STORE_SESSION_PROP+".redis.connectpoolsize");
			Config redisConfig = new Config();
			redisConfig.useSingleServer().setAddress(host)
										 .setConnectionPoolSize(Integer.valueOf(connectPoolSize))
										 .setDatabase(Integer.valueOf(database))
										 .setPassword(password);
			redissonClient = Redisson.create(redisConfig);
			RedissonStoreFactory storeFactory = new RedissonStoreFactory((Redisson)redissonClient);
			config.setStoreFactory(storeFactory);
		}
	}
}
